package com.gp.library.utils;

import android.util.Log;
import android.util.SparseArray;

import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

/**
 * Created by jackdeng on 2021/4/13.
 */


public class RxTimerUtil {
    private static final String TAG = "RxTimerUtil";
    private static Map<String, SparseArray<SoftReference<TimerObserver>>> timerObserverMap = new HashMap<>();

    public static int timer(long milliseconds, String tag, TimerObserver timerObserver) {
        Observable.timer(milliseconds, TimeUnit.MILLISECONDS, Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(timerObserver);
        return cacheTimerObservers(timerObserver, tag);
    }

    public static int timer(long initialDelay, long period, String tag, TimerObserver timerObserver) {
        Observable.interval(initialDelay, period, TimeUnit.MILLISECONDS, Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(timerObserver);
        return cacheTimerObservers(timerObserver, tag);
    }

    public static int intervalRange(int count, long period, String tag, TimerObserver timerObserver) {
        Observable.intervalRange(1, count, period, period, TimeUnit.MILLISECONDS, Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(timerObserver);
        return cacheTimerObservers(timerObserver, tag);
    }

    public static int countDown(int totalSec, long period, String tag, TimerObserver timerObserver) {
        Observable.intervalRange(totalSec, totalSec, period, period, TimeUnit.MILLISECONDS, Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(timerObserver);
        return cacheTimerObservers(timerObserver, tag);
    }

    public static void cancelGroupByTag(String tag) {
        SparseArray<SoftReference<TimerObserver>> timerObservers = timerObserverMap.get(tag);
        if (timerObservers == null || timerObservers.size() == 0) {
            return;
        }
        for (int i = 0; i < timerObservers.size(); i++) {
            cancelFromObserversByKey(timerObservers, timerObservers.keyAt(i));
        }
    }

    public static void cancelFromObserversByKey(SparseArray<SoftReference<TimerObserver>> timerObservers, int key) {
        if (timerObservers == null
                || timerObservers.get(key) == null
                || timerObservers.get(key).get() == null) {
            return;
        }
        TimerObserver timerObserver = timerObservers.get(key).get();
        timerObserver.cancel();
    }

    public static void cancelByKey(int key, String tag) {
        SparseArray<SoftReference<TimerObserver>> timerObservers = timerObserverMap.get(tag);
        if (timerObservers == null || timerObservers.size() == 0) {
            return;
        }
        cancelFromObserversByKey(timerObservers, key);
    }

    public abstract static class TimerObserver implements Observer<Long> {
        private Disposable disposable;
        private String tag;

        @Override
        public void onSubscribe(Disposable disposable) {
            this.disposable = disposable;
        }

        @Override
        public void onError(Throwable e) {
            cancel();
        }

        @Override
        public void onComplete() {
            cancel();
        }

        public void cancel() {
            int key = TimerObserver.this.hashCode();
            if (disposable != null && !disposable.isDisposed()) {
                disposable.dispose();
            }
            cleanCaches(key, tag);
        }

        public void setTag(String tag) {
            this.tag = tag;
        }
    }

    private static int cacheTimerObservers(TimerObserver timerObserver, String tag) {
        timerObserver.setTag(tag);
        int timerObserverKey = timerObserver.hashCode();
        Log.d(TAG, "timer  key:" + timerObserverKey + "     " + tag);
        SparseArray<SoftReference<TimerObserver>> timerObservers = timerObserverMap.get(tag);
        if (timerObservers == null) {
            timerObservers = new SparseArray<>();
        }
        timerObservers.put(timerObserverKey, new SoftReference<>(timerObserver));
        timerObserverMap.put(tag, timerObservers);
        return timerObserverKey;
    }

    private static void cleanCaches(int key, String tag) {
        if (StringUtils.isNullOrEmpty(tag)) {
            return;
        }
        Iterator<Map.Entry<String, SparseArray<SoftReference<TimerObserver>>>> iterator = timerObserverMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, SparseArray<SoftReference<TimerObserver>>> entry = iterator.next();
            String tag1 = entry.getKey();
            if (!tag1.equals(tag)) {
                continue;
            }
            SparseArray<SoftReference<TimerObserver>> timerObservers = entry.getValue();
            try {
                if (timerObservers == null) {
                    iterator.remove();
                    continue;
                }
                Log.d(TAG, "remove timerObserver key:" + key + "     " + tag);
                timerObservers.remove(key);
                if (timerObservers.size() == 0) {
                    Log.d(TAG, "remove 组 " + tag);
                    iterator.remove();
                }
            } catch (Exception e) {
                Log.e(TAG, "remove error:   key:" + key + "     " + tag);
            }
        }
    }
}
